package main;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;

import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.DisplayMode;

import com.jme.system.DisplaySystem;

public class ResolutionHandler {
	
	public static final String[] windowedResolutions = { "640x480","800x600","1024x768" };
	
	private static ResolutionHandler resHandler = new ResolutionHandler();
	
	private DisplayMode[] displayModes;
	private HashMap<String, String[]> depthsMap, freqsMap;
	private String[] sortedResolutions;
	
	private ResolutionHandler() {
		depthsMap = new HashMap<String, String[]>();
		freqsMap = new HashMap<String, String[]>();
	}
	
	public static ResolutionHandler get() { return resHandler; }
	
	public boolean init() {
		try {
			displayModes = org.lwjgl.opengl.Display.getAvailableDisplayModes();
		} catch(LWJGLException e) {
			return false;
		}
		
		getResolutions();
		
		return true;
	}
	
	private void getResolutions() {
		Collection<String> resolutions = new HashSet<String>();
		
		for(DisplayMode mode : displayModes) {
			if(modeTooSmall(mode)) continue;
			String modeString = getDisplayModeString(mode);
			if(!resolutions.contains(modeString)) {
				resolutions.add(modeString);
				getDepthsOf(modeString, new HashSet<String>());
				getFrequenciesOf(modeString, new HashSet<String>());
			}
		}
	}
	
	public void getFrequenciesOf(String res, Collection<String> freqs) {
		assert(freqs != null);
		
		for(DisplayMode mode : displayModes) {
			if(modeTooSmall(mode)) continue;
			String modeString = getDisplayModeString(mode);
			if(res.equals(modeString) && !freqs.contains(mode.getFrequency() + "")) 
				freqs.add(mode.getFrequency() + "");
		}
		if(freqs.isEmpty()) freqsMap.put(res, new String[] { "60" });
		else {
			String[] array = new String[freqs.size()];
			freqsMap.put(res, freqs.toArray(array));
		}
	}
	
	private void getDepthsOf(String res, Collection<String> depths) {
		assert(depths != null);
		
		for(DisplayMode mode : displayModes) {
			if(modeTooSmall(mode)) continue;
			
			String modeString = getDisplayModeString(mode);
			if(res.equals(modeString) 
			&& !depths.contains(mode.getBitsPerPixel() + "") 
			&& mode.getBitsPerPixel() >= 16) 
				depths.add(mode.getBitsPerPixel() + "");
		}
		if(depths.isEmpty()) depthsMap.put(res, new String[] { "24" });
		else {
			String[] array = new String[depths.size()];
			depthsMap.put(res, depths.toArray(array));
		}
	}
	
	private String getDisplayModeString(DisplayMode mode) {
		return mode.getWidth() + "x" + mode.getHeight();
	}
	
	private boolean modeTooSmall(DisplayMode mode) {
		return mode.getWidth() < 640;
	}
	
	public java.awt.DisplayMode getValidMode(int width, int height) {
		String modeString = getDisplayModeString(new DisplayMode(width, height));
		
		List<String> depths = new ArrayList<String>();
		getDepthsOf(modeString, depths);
		Collections.sort(depths, Collections.reverseOrder());
		
		List<String> freqs = new ArrayList<String>();
		getFrequenciesOf(modeString, freqs);
		Collections.sort(freqs, Collections.reverseOrder());
		
		int depth = 0, freq = 0;
		for(String freqString : freqs) {
			int f = Integer.valueOf(freqString).intValue();
			for(String depthString : depths) {
				int d = Integer.valueOf(depthString).intValue();
				if(DisplaySystem.getDisplaySystem().isValidDisplayMode(width, height, d, f)) {
					depth = d;
					freq = f;
					break;
				}
			}
		}
		
		java.awt.DisplayMode mode = new java.awt.DisplayMode(width, height, depth, freq);
		return mode;
	}
	
	public String[] getSortedResolutions() {
		if(sortedResolutions == null) {
			Collection<String> res = depthsMap.keySet();
			sortedResolutions = new String[res.size()];
			res.toArray(sortedResolutions);
			Arrays.sort(sortedResolutions, new ResolutionComparer());
		}
		return sortedResolutions;
	}
	
	public HashMap<String, String[]> getDepthMap() { return depthsMap; }
	
	public HashMap<String, String[]> getFrequencyMap() { return freqsMap; }
}